Khi làm việc với database, có rất nhiều các công việc cần làm đầu tiên như tạo cấu trúc bảng, cột, index, khóa chính, khóa ngoại, tạo dữ liệu test…, các công việc này thường tốn rất nhiều thời gian và cũng gây không ít cản trở lớn đặc biệt khi làm việc theo nhóm. Nhưng với Laravel mọi thứ trở lên đơn giản hơn với các công cụ như Laravel Migrate, Laravel Seeding và các lớp được xây dựng sẵn như Schema, Factory. Cơ sở dữ liệu cũng giống như mã nguồn cần được quản lý phiên bản và Laravel đã đưa ra một cách xử lý hết sức tinh tế cho công việc thật sự khó khăn này. Chúng ta sẽ cùng nhau tìm hiểu xem cách thức Laravel xử lý các công việc này như thế nào?
1. Laravel Migration tạo các phiên bản database
Laravel Migration là cách thức quản lý các phiên bản cơ sở dữ liệu (database version control) giúp cho làm việc nhóm với cơ sở dữ liệu dễ dàng trong việc chia sẻ và thay đổi các kiến trúc cơ sở dữ liệu (database schema). Trong phần này chúng ta sẽ nói nhiều đến Laravel Migrate là gì và sử dụng nó như thế nào, chủ đề về quản lý phiên bản database sẽ được bàn tới chi tiết hơn ở cuối bài viết này. ### 1.1 Tạo file migrate bằng câu lệnh Artisan
Để tạo một Laravel Migration chúng ta sử dụng câu lệnh artisan:
php artisan make:migration create_users_table
Khi đó một file migration sẽ được tạo ra trong thư mục database/migrations, trong tên các file migration có chứa thông tin thời gian tạo ra giúp cho Laravel sắp xếp và xác định được các file mirgration tại các thời điểm cần thiết. ### 1.2 Cấu trúc các file migration
File migration chứa Migration class với hai phương thức up() và down(), phương thức up() sử dụng để thêm các bảng, cột hoặc tạo một index cho cột nào đó trong CSDL. Phương thức down() sử dụng để làm những việc ngược lại với phương thức up(). Chúng ta cùng xem ví dụ sau:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('price');
$table->mediumText('content');
$table->boolean('active');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('products');
}
}
phương thức up() sẽ tạo ra một bảng products như trong ví dụ ở bài Xây dựng truy vấn với Laravel Query Builder, phương thức down() đơn giản là xóa bảng products khỏi database. ### 1.3 Thực hiện migration
Để chạy tất cả các migration có trong database/migraions sử dụng lệnh artisan như sau:
php artisan migrate
Các migration file có thể ảnh hưởng đến cấu trúc database hiện tại, do đó khi chạy lệnh trên có thể nó sẽ thông báo xác nhận chạy hay không, để ép buộc chạy migration mà không hiển thị thông báo sử dụng thêm tùy chọn –force
php artisan migrate --force
Trong quá trình migration có thể bạn muốn quay trở lại cấu trúc CSDL trước lúc thực thi các migration, hoàn toàn có thể được bằng cách sử dụng rollback và reset. Rollback thì cần cung cấp thêm tùy chọn –step là rollback lại bao nhiêu lần, còn reset sẽ đưa cấu trúc CSDL về thời điểm chưa có bất kỳ một migration nào.
php artisan migrate:rollback --step=3
php artisan migrate:reset
Ngoài ra, Laravel còn cung cấp migrate:refresh, đúng như tên gọi của nó, lệnh ra rollback toàn bộ các migrate và sau đó chạy lại lệnh migrate, tức là tương đương hai lệnh sau chạy lần lượt:
php artisan migrate:reset
php artisan migrate
2. Thao tác với bảng, cột CSDL bằng Laravel Schema
Laravel Schema là một lớp rất quan trọng trong thiết kế các thực thể cơ sở dữ liệu mà không cần phải viết bất kỳ một dòng lệnh SQL nào. Schema giúp xây dựng tạo ra, xóa, cập nhật các cột cũng như bảng trong cơ sở dữ liệu. Các thiết lập khác như index, khóa chính, khóa phụ cũng có thể thực hiện với Schema. Như vậy, thông qua các Schema chúng ta không cần thao tác trực tiếp với database và nó giúp cho ứng dụng của bạn có thể chuyển đổi dễ dàng từ hệ cơ sở dữ liệu này sang một hệ cơ sở dữ liệu khác chỉ bằng cách thiết lập các database driver trong cấu hình. Laravel hiện hỗ trợ 4 loại cơ sở dữ liệu là MySQL (của Oracle, phiên bản khác là MariaDB do cộng đồng MySQL trước đây chuyển sang), Postgres, SQLite, SQL Server (của Microsoft). Laravel cũng có thể làm việc với cơ sở dữ liệu Oracle tuy nhiên cần phải cài đặt thêm các gói thư viện, chúng ta sẽ bàn vấn đề này trong một bài viết khác. #### 2.1 Thao tác với bảng
Phương thức create() của lớp Schema giúp tạo ra một bảng dữ liệu trong cơ sở dữ liệu, nó có hai tham số:
- Tham số thứ nhất là một chuỗi chứa tên bảng dữ liệu.
- Tham số thứ hai là hàm Closure để khai báo các cột trong bảng dữ liệu.
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('price');
$table->mediumText('content');
$table->boolean('active');
$table->timestamps();
});
Khi tạo bảng có thể bảng đó đã tồn tại, chúng ta cần kiểm tra trước khi tạo, tương tự với các cột trong bảng thông qua các phương thức hasTable() và hasColumn():
if (!Schema::hasTable('products')) {
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->integer('price');
$table->mediumText('content');
$table->boolean('active');
$table->timestamps();
});
}
if (Schema::hasColumn('products', 'warranty_info')) {
//
}
Chúng ta cũng có thể thực hiện đổi tên bảng, xóa bảng
// Đổi tên bảng products thành san_pham
Schema::rename('products', 'san_pham');
// Xóa bảng products
Schema::drop('products');
// Xóa bảng products nếu nó tồn tại trong CSDL
Schema::dropIfExists('products');
2.2 Thao tác với cột dữ liệu
Để tạo cột, sử dụng tham số thứ 2 trong phương thức create() là đối tượng Blueprint để tạo bảng ở trên.
$table->increments('id');
$table->string('name');
$table->integer('price');
$table->mediumText('content');
$table->boolean('active');
$table->timestamps();
Chúng ta cùng lượt qua các dòng trong đoạn code trên, string() sẽ tạo ra cột có kiểu là VARCHAR, integer() tạo ra cột có kiểu INT… Ngoài ra, có một số phương thức đặc biệt như increments() tạo trường khóa chính và tự động tăng, timestamps() tạo ra các cột created_at, updated_at chứa thời gian tạo và cập nhật dữ liệu trong bảng, các dữ liệu thời gian này được cập nhật tự động khi bạn thêm hoặc thay đổi một bản ghi. Danh sách các dạng cột có thể tạo ra như sau:
Câu lệnh | Mô tả |
---|---|
$table->bigIncrements('id'); | Incrementing ID (primary key) using a “UNSIGNED BIG INTEGER” equivalent. |
$table->bigInteger('votes'); | BIGINT equivalent for the database. |
$table->binary('data'); | BLOB equivalent for the database. |
$table->boolean('confirmed'); | BOOLEAN equivalent for the database. |
$table->char('name', 4); | CHAR equivalent with a length. |
$table->date('created_at'); | DATE equivalent for the database. |
$table->dateTime('created_at'); | DATETIME equivalent for the database. |
$table->dateTimeTz('created_at'); | DATETIME (with timezone) equivalent for the database. |
$table->decimal('amount', 5, 2); | DECIMAL equivalent with a precision and scale. |
$table->double('column', 15, 8); | DOUBLE equivalent with precision, 15 digits in total and 8 after the decimal point. |
$table->enum('choices', ['foo', 'bar']); | ENUM equivalent for the database. |
$table->float('amount', 8, 2); | FLOAT equivalent for the database, 8 digits in total and 2 after the decimal point. |
$table->increments('id'); | Incrementing ID (primary key) using a “UNSIGNED INTEGER” equivalent. |
$table->integer('votes'); | INTEGER equivalent for the database. |
$table->ipAddress('visitor'); | IP address equivalent for the database. |
$table->json('options'); | JSON equivalent for the database. |
$table->jsonb('options'); | JSONB equivalent for the database. |
$table->longText('description'); | LONGTEXT equivalent for the database. |
$table->macAddress('device'); | MAC address equivalent for the database. |
$table->mediumIncrements('id'); | Incrementing ID (primary key) using a “UNSIGNED MEDIUM INTEGER” equivalent. |
$table->mediumInteger('numbers'); | MEDIUMINT equivalent for the database. |
$table->mediumText('description'); | MEDIUMTEXT equivalent for the database. |
$table->morphs('taggable'); | Adds unsigned INTEGER taggable_id and STRINGtaggable_type . |
$table->nullableMorphs('taggable'); | Nullable versions of the morphs() columns. |
$table->nullableTimestamps(); | Nullable versions of the timestamps() columns. |
$table->rememberToken(); | Adds remember_token as VARCHAR(100) NULL. |
$table->smallIncrements('id'); | Incrementing ID (primary key) using a “UNSIGNED SMALL INTEGER” equivalent. |
$table->smallInteger('votes'); | SMALLINT equivalent for the database. |
$table->softDeletes(); | Adds nullable deleted_at column for soft deletes. |
$table->string('email'); | VARCHAR equivalent column. |
$table->string('name', 100); | VARCHAR equivalent with a length. |
$table->text('description'); | TEXT equivalent for the database. |
$table->time('sunrise'); | TIME equivalent for the database. |
$table->timeTz('sunrise'); | TIME (with timezone) equivalent for the database. |
$table->tinyInteger('numbers'); | TINYINT equivalent for the database. |
$table->timestamp('added_on'); | TIMESTAMP equivalent for the database. |
$table->timestampTz('added_on'); | TIMESTAMP (with timezone) equivalent for the database. |
$table->timestamps(); | Adds nullable created_at and updated_at columns. |
$table->timestampsTz(); | Adds nullable created_at and updated_at (with timezone) columns. |
$table->unsignedBigInteger('votes'); | Unsigned BIGINT equivalent for the database. |
$table->unsignedInteger('votes'); | Unsigned INT equivalent for the database. |
$table->unsignedMediumInteger('votes'); | Unsigned MEDIUMINT equivalent for the database. |
$table->unsignedSmallInteger('votes'); | Unsigned SMALLINT equivalent for the database. |
$table->unsignedTinyInteger('votes'); | Unsigned TINYINT equivalent for the database. |
$table->uuid('id'); | UUID equivalent for the database. |
Ngoài ra các thiết lập khác có thể đưa vào khi tạo cột như: cột có thể null, comment về cột, tạo cột sau một cột khác… Danh sách các thiết lập khác như sau:
Modifier | Description |
---|---|
->after('column') | Place the column “after” another column (MySQL Only) |
->comment('my comment') | Add a comment to a column |
->default($value) | Specify a “default” value for the column |
->first() | Place the column “first” in the table (MySQL Only) |
->nullable() | Allow NULL values to be inserted into the column |
->storedAs($expression) | Create a stored generated column (MySQL Only) |
->unsigned() | Set integer columns to UNSIGNED |
->virtualAs($expression) | Create a virtual generated column (MySQL Only) |
Laravel cũng cho phép thay đổi các tham số trong dạng cột, tên cột… để thực hiện trước tiên phải cài đặt một gói doctrine/dbal, sử dụng lệnh composer
composer require doctrine/dbal
Ok, sau khi cài đặt gói doctrine/dbal, chúng ta có thể thay đổi thuộc tính của cột như sau:
Schema::table('products', function (Blueprint $table) {
$table->string('name', 50)->change();
$table->string('warranty')->nullable()->change();
});
Hoặc thay đổi tên một cột trong bảng:
Schema::table('products', function (Blueprint $table) {
$table->renameColumn('warranty_info', 'thong_tin_bao_hanh');
});
Xóa trên cột trong bảng
Schema::table('products', function (Blueprint $table) {
$table->dropColumn(['price', 'content', 'warranty_info']);
});
2.3 Thao tác liên quan đến index
Để tạo một index chúng ta sử dụng cú pháp:
$table->string('product_code')->unique();
Hoặc tạo index sau khi đã tạo cột
$table->unique('product_code');
Tạo index cho nhiều cột và đặt tên cho index
$table->index(['price', 'product_code'], 'price_code_index');
Danh sách các dạng index cho phép
Command | Description |
---|---|
$table->primary('id'); | Add a primary key. |
$table->primary(['first', 'last']); | Add composite keys. |
$table->unique('email'); | Add a unique index. |
$table->unique('state', 'my_index_name'); | Add a custom index name. |
$table->unique(['first', 'last']); | Add a composite unique index. |
$table->index('state'); | Add a basic index. |
Xóa một index khỏi CSDL
Command | Description |
---|---|
$table->dropPrimary('users_id_primary'); | Drop a primary key from the “users” table. |
$table->dropUnique('users_email_unique'); | Drop a unique index from the “users” table. |
$table->dropIndex('geo_state_index'); | Drop a basic index from the “geo” table. |
2.4 Các thiết lập khóa ngoại
Laravel cũng cho phép thiết lập ràng buộc khóa ngoại, ví dụ: trong bảng products có trường admin_id là trường chứa ID của quản trị viên đã tạo sản phẩm, trường admin_id sẽ tham chiếu sang bảng admin chứa danh sách các quản trị viên hệ thống.
Schema::table('products', function (Blueprint $table) {
$table->integer('admin_id')->unsigned();
$table->foreign('admin_id')->references('id')->on('admin');
});
Khi đã thiết lập khóa ngoại, chúng ta có thể thiết lập các hành động như update hay delete
$table->foreign('admin_id')
->references('id')->on('admin')
->onDelete('cascade');
Chúng ta cũng có thể xóa khóa ngoại bằng câu lệnh
$table->dropForeign('products_admin_id_foreign');
Chú ý, tên khóa ngoại giống như quy ước đặt tên trong index, tên bảng + tên cột + hậu tố _foreign. Laravel cho phép enable hoặc disable khóa ngoại trong CSDL bằng lệnh
Schema::enableForeignKeyConstraints();
Schema::disableForeignKeyConstraints();
3. Đưa dữ liệu vào database bằng Laravel Seeding
Có một số tình huống chúng ta cần đưa dữ liệu hàng loạt vào database như tạo dữ liệu test ứng dụng, đổ dữ liệu hàng loạt phục vụ cho chuyển đổi hệ thống… Laravel cung cấp các phương thức đơn giản để thực hiện bằng cách tạo các các lớp mở rộng của lớp Seeder. Các lớp Seeder được lưu trong thư mục database/seeds. Chúng ta có thể tạo bằng tay một file có phần mở rộng php hoặc tạo tự động một Seeder bằng cách sử dụng lệnh artisan như sau:
php artisan make:seeder ProductsTableSeeder
Mỗi lớp Seeder sẽ có một phương thức run(), phương thức này sẽ được thực thi khi thực hiện câu lệnh
php artisan db:seed
``
Câu lệnh artisan này sẽ thực thi tất cả các lớp Seeder có trong thư mục database/seeds, nếu bạn không muốn thực thi tất cả, bạn có thể chỉ địch đích danh một Seeder cần thực thi:
```{.language-php}
php artisan db:seed --class=ProductsTableSeeder
Dữ liệu cần đổ vào database sẽ được đưa vào phương thức run() của Seeder mặc định là DatabaseSeeder (đường dẫn đầy đủ là database/seeds/DatabaseSeeder.php), ví dụ sau thực hiện đưa dữ liệu 4 sản phẩm vào cơ sở dữ liệu.
<?php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('products')->insert([
['name' => str_random(255), 'price' => '300000', 'content' => str_random(255), 'active' => 1],
['name' => str_random(255), 'price' => '400000', 'content' => str_random(255), 'active' => 1],
['name' => str_random(255), 'price' => '500000', 'content' => str_random(255), 'active' => 1],
['name' => str_random(255), 'price' => '600000', 'content' => str_random(255), 'active' => 1],
]);
}
}
Trong phương thức run() của Seeder mặc định DatabaseSeeder chúng ta cũng có thể gọi đến các Seeder khác do chúng ta tạo ra:
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->call(UsersTableSeeder::class);
$this->call(PostsTableSeeder::class);
$this->call(CommentsTableSeeder::class);
}
4. Tạo dữ liệu test cho ứng dụng
Laravel Seeding là một trong những tính năng rất hay của Laravel giúp đưa dữ liệu vào Cơ sở dữ liệu, tuy nhiên khi tạo dữ liệu kiểm thử ứng dụng, chúng ta cần tạo ra nhiều bản ghi với các dữ liệu được tạo ra ngẫu nhiên và Faker là một thư viện tuyệt vời cho mục đích này. Faker là một thư viện PHP được sử dụng để tạo ra dữ liệu giả cho mục đích kiểm thử ứng dụng.
4.1 Cài đặt Faker trong Laravel
Laravel 5 trở lên cài đặt Faker mặc định, do đó bạn không cần cài đặt gì thêm. Bạn có thể kiểm tra các thư viện được cài đặt trong file composer.json nằm trong thư mục gốc.
"require-dev": {
"fzaninotto/faker": "~1.4",
"mockery/mockery": "0.9.*",
"phpunit/phpunit": "~5.7"
},
fzaninotto/faker chính là thư viện Faker cần cài đặt. Nếu bạn đang sử dụng các phiên bản thấp hơn có thể cài đặt Faker bằng composer như sau:
composer require fzaninotto/faker --dev
Faker có thể sử dụng để sinh ra các dữ liệu giả như số, văn bản text, thông tin người dùng (tên, email, giới tính…), địa chỉ, số điện thoại, dữ liệu thời gian, màu sắc, ảnh… Chi tiết các định dạng dữ liệu giả có thể được sinh ra bởi Faker bạn có thể xem tại đây.
4.2. Sử dụng Faker tạo dữ liệu test
Đầu tiên, chúng ta tạo một project Laravel để thực hành với Faker.
composer create-project laravel/laravel faker
Hoặc nếu bạn đã có một dự án Laravel sẵn chúng ta chuyển sang bước tiếp theo. Mở file routes/web.api (Chú ý: Laravel 5.3 trở lên chuyển file route.php trong app\Http sang thư mục routes với web.api là các route cho web) để thực hành với Faker:
Route::get('/customers', function(){
$faker = Faker\Factory::create();
$limit = 1000;
$customers = [];
for ($i = 0; $i < $limit; $i++) {
$customers[$i] = [
'Họ và tên' => $faker->name,
'Email' => $faker->unique()->email,
'Số điện thoại' => $faker->phoneNumber,
'Website' => $faker->domainName,
'Tuổi' => $faker->numberBetween(20,100),
'Địa chỉ' => $faker->address
];
}
return response()->json($customers);
});
Thử truy nhập vào đường dẫn /customers chúng ta sẽ thấy kết quả thông tin giả của 1000 khách hàng được tạo ra như sau:
![](https://danatec.vn/wp-content/uploads/2023/03/9.png)
Bạn thấy đấy, dữ liệu giả được tạo ra rất nhanh, trong phát triển ứng dụng, việc tạo ra các dữ liệu giả để test là rất cần thiết, sẽ thật mất thời gian khi tự điền các dữ liệu giả vào phải không. Faker có thể tạo ra rất nhiều các dạng dữ liệu giả khác nhau, dưới đây là một số ví dụ về dữ liệu giả được tạo ra:
$faker->randomDigit;
$faker->numberBetween(1,100);
$faker->word;
$faker->paragraph;
$faker->lastName;
$faker->city;
$faker->year;
$faker->domainName;
$faker->creditCardNumber;
Ngoài ra, Faker cho phép bạn tạo dữ liệu giả không chỉ ngôn ngữ tiếng Anh mà còn với nhiều ngôn ngữ khác:
$faker = Faker\Factory::create('fr_FR');
Khi đó dữ liệu giả sẽ có dạng tiếng Pháp, Faker hỗ trợ rất nhiều các ngôn ngữ, rất tiếc là không có tiếng Việt nhưng cũng không vấn đề gì vì đây chỉ là dữ liệu test thôi.
4.3. Model Factory tạo dữ liệu test phức tạp
4.3.1 Định nghĩa Model Factory
Trong ví dụ ở trên bạn đã tạo ra 1000 khách hàng giả để test, tuy nhiên trong thực tế chúng ta cần những dữ liệu giả phức tạp hơn. Chúng ta cùng xem ví dụ về xây dựng một diễn đàn (Xem bài Xây dựng Forum dạng SPA với Laravel và Vue.js để hiểu hơn), trong đó có danh mục (category) và các chủ đề (topic), giữa category và topic có mối quan hệ 1-n với nhau, do đó khi tạo ra topic thì topic đó cần được trỏ về 1 category. Đây là một ví dụ cho thấy dữ liệu giả cũng cần có quan hệ. Rất may, Laravel có Model Factory để làm việc này, trước đây là một gói thư viện riêng do Jeffrey Way thành viên của Laracast viết và sau đó được Laravel đưa vào core như là một phần của Laravel. Để sử dụng các Model Factory chúng ta cần định nghĩa chúng trong file database/factories/ModelFactory.php. Laravel đã tạo sẵn một biến toàn cục $factory giúp mở rộng các định nghĩa Model Factory.
$factory->define(App\Category::class, function (Faker\Generator $faker) {
return [
'name' => implode(' ', $faker->words(2)),
'description' => $faker->sentence(),
];
});
$factory->define(App\Topic::class, function (Faker\Generator $faker) {
return [
'category_id' => null,
'user_id' => 1,
'title' => $faker->sentence,
'body' => $faker->paragraph(7),
'views' => $faker->numberBetween(0, 10000),
'created_at' => $faker->datetimeBetween('-5 months'),
];
});
$factory->define(App\Post::class, function (Faker\Generator $faker) {
return [
'topic_id' => null,
'user_id' => 1,
'body' => $faker->sentence,
];
});
Phương thức define() với hai tham số, một là tên đầy đủ của model, hai là một instance của Faker và nó trả về một mảng các thuộc tính của đối tượng với dữ liệu có thể được tạo giả.
4.3.2 Tạo ra các đối tượng có dữ liệu giả bằng Model Factory
Sau khi định nghĩa xong chúng ta có thể tạo ra một đối tượng với các thuộc tính có dữ liệu giả bằng cách gọi phương thức create() trong các Seeding.
$category = factory(Category::class)->create();
đối tượng với dữ liệu giả được tạo ra và create() cũng thực hiện ghi dữ liệu này xuống database luôn. Nếu muốn tạo ra nhiều hơn 1 đối tượng, truyền thêm tham số vào như sau:
$category = factory(Category::class, 5)->create();
Tạo 5 danh mục và cũng ghi luôn xuống database. Đôi khi chúng ta chỉ muốn tạo ra đối tượng dữ liệu giả mà không ghi xuống database, bạn có thể sử dụng phương thức make() thay cho create():
$category = factory(Category::class)->make();
4.3.3 Định nghĩa các dạng đối tượng khác nhau
Có thể bạn muốn tạo ra các dạng đối tượng khác nhau khi sử dụng Model Factory, ví dụ một bài viết dài và một bài viết ngắn, chúng ta cùng xem đoạn code sau:
$factory->defineAs(App\Post::class, 'short-post', function ($faker) {
return [
'title' => $faker->sentence,
'body' => $faker->paragraph
];
});
$factory->defineAs(App\Post::class, 'long-post', function ($faker) {
return [
'title' => $faker->sentence,
'body' => implode("\n\n", $faker->paragraphs(10))
];
});
4.3.4 Tạo các đối tượng có quan hệ
Với các đối tượng có quan hệ, chúng ta có thể thực hiện tạo ra các đối tượng này, ví dụ dưới đây tạo ra 5 danh mục và mỗi danh mục tạo ra 5 chủ đề liên quan.
factory(Category::class, 5)->create()->each(function($c) {
$c->topics()->saveMany(
factory(Topic::class, 50)->create([
'category_id' => $c->id
])
);
});
Chú ý, đoạn code trên chỉ chạy được khi đối tượng Category đã định nghĩa topics() với mối quan hệ là hasMany(). Xem phần Xử lý quan hệ trong database với Laravel Eloquent Relationship để hiểu về các quan hệ 1-1, 1-n, n-n có sử dụng các phương thức hasMany().
5. Làm việc theo nhóm với phiên bản cơ sở dữ liệu
Quản lý phiên bản trong lập trình là một công việc tiêu tốn nhiều thời gian, tài nguyên và công sức, database cũng giống như mã nguồn đều cần phải được quản lý phiên bản. Trong thực tế, khi xuất hiện một yêu cầu mới hoặc chỉnh sửa một yêu cầu trước đó, các lập trình viên có thể phải thay đổi một số bảng trong database, tuy nhiên có thể nhiều các thành viên khác không biết việc này. Laravel Migrate ra đời nhằm mục đích như vậy, nó giúp cho làm việc theo nhóm có thể dễ dàng chia sẻ, thay đổi kiến trúc của cơ sở dữ liệu.
Các bước thực hiện để quản lý phiên bản cơ sở dữ liệu như sau:
- Khi có yêu cầu thay đổi kiến trúc cơ sở dữ liệu (database schema) thực hiện thông qua Laravel Migrate:
- Tạo file migrate với câu lệnh php artisan make:migrate ten_file_migrate, chú ý khi tạo ra file migrate, hệ thống đã tự động đưa thêm yếu tố thời gian vào tên file, do đó có thể biết được file migrate này tạo ra khi nào, chúng ta chỉ cần đặt tên file sao cho gợi nhớ ví dụ: create_users_table, alter_users_table…
- Các thay đổi cần thực hiện trên kiến trúc cơ sở dữ liệu cần được chuyển thành các câu lệnh và đưa vào phương thức up().
- Trong phương thức down() cần viết code để rollback lại những gì đã thực hiện trong phương thức up(), nếu không thực hiện công việc này cũng không thể quản lý phiên bản cơ sở dữ liệu một cách tự động được.
Bước này là rất quan trọng, thông thường với lập trình viên làm một mình hoặc nhóm nhỏ thường điều chỉnh trực tiếp trên cơ sở dữ liệu để tiết kiệm thời gian, nhưng nếu làm vậy chúng ta sẽ không có thông tin để quản lý phiên bản.
- Sau khi thực hiện viết hoặc thay đổi code trong ứng dụng, thực hiện commit code trong đó có cả file migrate.
- Như vậy khi cần thiết chúng ta có thể quay lại phiên bản đã commit, khi đó trong thư mục database/migrations chứa các file migrate điều chỉnh kiến trúc cơ sở dữ liệu đến thời điểm cần, chúng ta chỉ cần thực hiện chạy php artisan migrate là nó sẽ build lại toàn bộ kiến trúc cơ sở dữ liệu. Chú ý: trên đây chúng ta mới chỉ nói đến bộ khung cho cơ sở dữ liệu, các dữ liệu kiểm thử cho ứng dụng chúng ta cũng cần thực hiện thông qua Laravel Seeding và đưa vào danh sách các file cần commit cho lần sửa đổi này. Như vậy, khi quay lại phiên bản cũ chúng ta hoàn toàn có thể chạy câu lệnh php artisan db:seed sau khi đã thực hiện migrate để có dữ liệu test.
6. Lời kết
Như vậy chúng ta đã làm quen được với các công cụ làm việc với cơ sở dữ liệu của Laravel như Laravel Migrate, Laravel Seeding, Schema, Model Factory, Faker… cũng như các ý tưởng rất hay khi đưa các công việc làm ở phía cơ sở dữ liệu lên trên mức lập trình giúp chúng ta hoàn toàn có thể chuyển đổi dễ dàng giữa các hệ quản trị cơ sở dữ liệu và cũng nhờ đó việc quản lý phiên bản trong cơ sở dữ liệu trở lên đơn giản hơn. Chúng ta đã đi qua được phần đầu trong làm việc với database, trong phần tiếp theo chúng ta sẽ làm quen với một khái niệm mới là Laravel Eloquent Model sử dụng kỹ thuật ORM (Object Relational Mapping) giúp mapping giữa các bảng dữ liệu thành các đối tượng trong lập trình.